#include "w25qxx.h"
#include "init.h"
extern spi_t *spi;
/**

 * @brief  读取FLASH ID
 * @param  无
 * @retval FLASH ID
 */
void SPI_FLASH_ReadID(u8 *MID, u16 *DID)
{
	/* 开始通讯：CS低电平 */
	FLASH_SPI_CS_LOW();

	/* 发送JEDEC指令，读取ID */
	// SPI_FLASH_SendByte(W25X_JedecDeviceID);
	spi->PspiFlashSendByte(W25X_JedecDeviceID);
	/* 读取一个字节数据 */
	//*MID = SPI_FLASH_SendByte(Dummy_Byte);
	*MID = spi->PspiFlashSendByte(Dummy_Byte);
	/* 读取一个字节数据 */
	//*DID = SPI_FLASH_SendByte(Dummy_Byte);
	*DID = spi->PspiFlashSendByte(Dummy_Byte);
	*DID <<= 8;
	/* 读取一个字节数据 */
	//*DID |= SPI_FLASH_SendByte(Dummy_Byte);
	*DID |= spi->PspiFlashSendByte(Dummy_Byte);
	/* 停止通讯：CS高电平 */
	FLASH_SPI_CS_HIGH();
}

/**
 * @brief  向FLASH发送 写使能 命令
 * @param  none
 * @retval none
 */
void SPI_FLASH_WriteEnable(void)
{
	/* 通讯开始：CS低 */
	FLASH_SPI_CS_LOW();
	/* 发送写使能命令*/
	//SPI_FLASH_SendByte(W25X_WriteEnable);
	spi->PspiFlashSendByte(W25X_WriteEnable);
	/*通讯结束：CS高 */
	FLASH_SPI_CS_HIGH();
}
/* WIP(busy)标志，FLASH内部正在写入 */
#define WIP_Flag 0x01

/**
 * @brief  等待WIP(BUSY)标志被置0，即等待到FLASH内部数据写入完毕
 * @param  none
 * @retval none
 */
void SPI_FLASH_WaitForWriteEnd(void)
{
	u8 FLASH_Status = 0;

	/* 选择 FLASH: CS 低 */
	FLASH_SPI_CS_LOW();

	/* 发送 读状态寄存器 命令 */
	//SPI_FLASH_SendByte(W25X_ReadStatusReg);
	spi->PspiFlashSendByte(W25X_ReadStatusReg);
	/* 若FLASH忙碌，则等待 */
	do
	{
		/* 读取FLASH芯片的状态寄存器 */
		FLASH_Status = spi->PspiFlashSendByte(Dummy_Byte);
	} while ((FLASH_Status & WIP_Flag) == SET); /* 正在写入标志 */

	/* 停止信号  FLASH: CS 高 */
	FLASH_SPI_CS_HIGH();
}

/**
 * @brief  擦除FLASH扇区
 * @param  SectorAddr：要擦除的扇区地址
 * @retval 无
 */
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
	/* 发送FLASH写使能命令 */
	SPI_FLASH_WriteEnable();
	SPI_FLASH_WaitForWriteEnd();
	/* 擦除扇区 */
	/* 选择FLASH: CS低电平 */
	FLASH_SPI_CS_LOW();
	/* 发送扇区擦除指令*/
	//SPI_FLASH_SendByte(W25X_SectorErase);
	spi->PspiFlashSendByte(W25X_SectorErase);
	/*发送擦除扇区地址的高位*/
	//SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
	spi->PspiFlashSendByte((SectorAddr & 0xFF0000) >> 16);
	/* 发送擦除扇区地址的中位 */
	//SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
	spi->PspiFlashSendByte((SectorAddr & 0xFF00) >> 8);
	/* 发送擦除扇区地址的低位 */
	//SPI_FLASH_SendByte(SectorAddr & 0xFF);
	spi->PspiFlashSendByte(SectorAddr & 0xFF);
	/* 停止信号 FLASH: CS 高电平 */
	FLASH_SPI_CS_HIGH();
	/* 等待擦除完毕*/
	SPI_FLASH_WaitForWriteEnd();
}

/**
 * @brief  对FLASH按页写入数据，调用本函数写入数据前需要先擦除扇区
 * @param  pBuffer，要写入数据的指针
 * @param WriteAddr，写入地址
 * @param  NumByteToWrite，写入数据长度，必须小于等于页大小
 * @retval 无
 */
void SPI_FLASH_PageWrite(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
	/* 发送FLASH写使能命令 */
	SPI_FLASH_WriteEnable();

	/* 选择FLASH: CS低电平 */
	FLASH_SPI_CS_LOW();
	/* 写送写指令*/
	//SPI_FLASH_SendByte(W25X_PageProgram);
	spi->PspiFlashSendByte(W25X_PageProgram);
	/*发送写地址的高位*/
	//SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
	spi->PspiFlashSendByte((WriteAddr & 0xFF0000) >> 16);
	/*发送写地址的中位*/
	//SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
	spi->PspiFlashSendByte((WriteAddr & 0xFF00) >> 8);
	/*发送写地址的低位*/
	//SPI_FLASH_SendByte(WriteAddr & 0xFF);
	spi->PspiFlashSendByte(WriteAddr & 0xFF);
	if (NumByteToWrite > SPI_FLASH_PerWritePageSize)
	{
		NumByteToWrite = SPI_FLASH_PerWritePageSize;
		DBG("SPI_FLASH_PageWrite too large!");
	}

	/* 写入数据*/
	while (NumByteToWrite--)
	{
		/* 发送当前要写入的字节数据 */
		//SPI_FLASH_SendByte(*pBuffer);
		spi->PspiFlashSendByte(*pBuffer);
		/* 指向下一字节数据 */
		pBuffer++;
	}

	/* 停止信号 FLASH: CS 高电平 */
	FLASH_SPI_CS_HIGH();
	/* 等待写入完毕*/
	SPI_FLASH_WaitForWriteEnd();
}

/**
 * @brief  对FLASH写入数据，调用本函数写入数据前需要先擦除扇区
 * @param   pBuffer，要写入数据的指针
 * @param  WriteAddr，写入地址
 * @param  NumByteToWrite，写入数据长度
 * @retval 无
 */
void SPI_FLASH_BufferWrite(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
	u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

	/*mod运算求余，若writeAddr是SPI_FLASH_PageSize整数倍，
	运算结果Addr值为0*/
	Addr = WriteAddr % SPI_FLASH_PageSize;

	/*差count个数据值，刚好可以对齐到页地址*/
	count = SPI_FLASH_PageSize - Addr;
	/*计算出要写多少整数页*/
	NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
	/*mod运算求余，计算出剩余不满一页的字节数*/
	NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

	/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
	if (Addr == 0)
	{
		/* NumByteToWrite < SPI_FLASH_PageSize */
		if (NumOfPage == 0)
		{
			SPI_FLASH_PageWrite(pBuffer, WriteAddr,
								NumByteToWrite);
		}
		else /* NumByteToWrite > SPI_FLASH_PageSize */
		{
			/*先把整数页都写了*/
			while (NumOfPage--)
			{
				SPI_FLASH_PageWrite(pBuffer, WriteAddr,
									SPI_FLASH_PageSize);
				WriteAddr += SPI_FLASH_PageSize;
				pBuffer += SPI_FLASH_PageSize;
			}
			/*若有多余的不满一页的数据，把它写完*/
			SPI_FLASH_PageWrite(pBuffer, WriteAddr,
								NumOfSingle);
		}
	}
	/* 若地址与 SPI_FLASH_PageSize 不对齐  */
	else
	{
		/* NumByteToWrite < SPI_FLASH_PageSize */
		if (NumOfPage == 0)
		{
			/*当前页剩余的count个位置比NumOfSingle小，一页写不完*/
			if (NumOfSingle > count)
			{
				temp = NumOfSingle - count;
				/*先写满当前页*/
				SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

				WriteAddr += count;
				pBuffer += count;
				/*再写剩余的数据*/
				SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
			}
			else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
			{
				SPI_FLASH_PageWrite(pBuffer, WriteAddr,
									NumByteToWrite);
			}
		}
		else /* NumByteToWrite > SPI_FLASH_PageSize */
		{
			/*地址不对齐多出的count分开处理，不加入这个运算*/
			NumByteToWrite -= count;
			NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
			NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

			/* 先写完count个数据，为的是让下一次要写的地址对齐 */
			SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

			/* 接下来就重复地址对齐的情况 */
			WriteAddr += count;
			pBuffer += count;
			/*把整数页都写了*/
			while (NumOfPage--)
			{
				SPI_FLASH_PageWrite(pBuffer, WriteAddr,
									SPI_FLASH_PageSize);
				WriteAddr += SPI_FLASH_PageSize;
				pBuffer += SPI_FLASH_PageSize;
			}
			/*若有多余的不满一页的数据，把它写完*/
			if (NumOfSingle != 0)
			{
				SPI_FLASH_PageWrite(pBuffer, WriteAddr,
									NumOfSingle);
			}
		}
	}
}
/**
 * @brief  读取FLASH数据
 * @param  pBuffer，存储读出数据的指针
 * @param   ReadAddr，读取地址
 * @param   NumByteToRead，读取数据长度
 * @retval 无
 */
void SPI_FLASH_BufferRead(u8 *pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
	/* 选择FLASH: CS低电平 */
	FLASH_SPI_CS_LOW();

	/* 发送 读 指令 */
	//SPI_FLASH_SendByte(W25X_ReadData);
	spi->PspiFlashSendByte(W25X_ReadData);
	/* 发送 读 地址高位 */
	//SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
	spi->PspiFlashSendByte((ReadAddr & 0xFF0000) >> 16);
	/* 发送 读 地址中位 */
	//SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
	spi->PspiFlashSendByte((ReadAddr & 0xFF00) >> 8);
	/* 发送 读 地址低位 */
	//SPI_FLASH_SendByte(ReadAddr & 0xFF);
	spi->PspiFlashSendByte(ReadAddr & 0xFF);
	/* 读取数据 */
	while (NumByteToRead--)
	{
		/* 读取一个字节*/
		//*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
		*pBuffer = spi->PspiFlashSendByte(Dummy_Byte);
		/* 指向下一个字节缓冲区 */
		pBuffer++;
	}

	/* 停止信号 FLASH: CS 高电平 */
	FLASH_SPI_CS_HIGH();
}
w25qxx_t *w25qxxCreate(void)
{
	static w25qxx_t w25qxx;
	w25qxx.PspiFlashReadId=SPI_FLASH_ReadID;
	w25qxx.PspiFlashSectorErase=SPI_FLASH_SectorErase;
	w25qxx.PspiFlashBufferWrite=SPI_FLASH_BufferWrite;
	w25qxx.PspiFlashPageWrite=SPI_FLASH_PageWrite;
	w25qxx.PspiFlashBufferRead=SPI_FLASH_BufferRead;
	return &w25qxx;
}

#if ELECT
void w25_test()
{
	u8 MID;
	u16 DID;
	printf("\r\n w25 test \r\n");
	char *Tx_Buff = "hello world";
	u8 Rx_Buffer[100] = {0};
	/* 8M串行flash W25Q64初始化 */
	SPI_FLASH_Init();

	/* 获取 SPI Flash ID */
	SPI_FLASH_ReadID(&MID, &DID);
	printf("\r\n FlashID is 0x%X,Manufacturer Device ID is 0x%X\r\n", MID, DID);
	if (DID == 0x4017)
	{
		printf("\r\n flash W25Q64 !\r\n");

		/* 擦除将要写入的 SPI FLASH 扇区，FLASH写入前要先擦除 */
		// 这里擦除4K，即一个扇区，擦除的最小单位是扇区
		SPI_FLASH_SectorErase(0x00000000);

		/* 将发送缓冲区的数据写到flash中 */
		// 这里写一页，一页的大小为256个字节
		printf("\r\nwrite data:%s \r\n", Tx_Buff);
		SPI_FLASH_BufferWrite((u8 *)Tx_Buff, 0x00000000, strlen(Tx_Buff));

		/* 将刚刚写入的数据读出来放到接收缓冲区中 */
		SPI_FLASH_BufferRead(Rx_Buffer, 0x00000000, sizeof(Rx_Buffer));
		printf("\r\n read data:%s\r\n", Rx_Buffer);

		/* 检查写入的数据与读出的数据是否相等 */
	}
}
#endif
